using System;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using System.Diagnostics;
using System.Data;
using Csla;
using Csla.Data;
using System.Data.Common;
using Microsoft.Practices.EnterpriseLibrary.Data;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

namespace Asah.SiteWorx.Search
{
	/// <summary>
	/// Properties are used to store search data of the user
	/// </summary>
	public class UserSearch
	{
		private int _totalFilesSearched = 0;
		private int _totalFilesFound = 0;
		private SearchCriteria _searchCriteria = SearchCriteria.AllWords;
		private string _searchWords = "";
		private DataSet _dstPages;
		private System.Web.UI.Page _page = new System.Web.UI.Page();
		public UserSearch()
		{
			//
			// TODO: Add constructor logic here
			//
		}

		/// <summary>
		/// Retreive TotalFilesSearched of the site
		/// </summary>
		public int TotalFilesSearched
		{
			get
			{
				return _totalFilesSearched;
			}
		}

		/// <summary>
		/// Retreive TotalFilesFound of the site
		/// </summary>
		public int TotalFilesFound
		{
			get
			{
				return _totalFilesFound;
			}
		}

		/// <summary>
		/// Retreive data of tzh entire site of the site
		/// </summary>
		public DataSet PageDataset
		{
			get
			{
				return _dstPages;
			}
		}

		/// <summary>
		/// Assign and retreive SearchCriteria of the site
		/// </summary>
		public SearchCriteria SearchCriteria
		{
			get
			{
				return _searchCriteria;
			}
			set
			{
				_searchCriteria = value;
			}
		}

		/// <summary>
		/// Assign and retreive SearchWords of the site
		/// </summary>
		public string SearchWords
		{
			get
			{
				return _searchWords;
			}
			set
			{
				_searchWords = value;
			}
		}

		/// <summary>
		/// Search the entire site
		/// </summary>
		public DataSet Search(string targetDirectory)
		{
			//If the site is in English then use the server HTML encode method
			if (Site.EnglishLanguage == true)
				_searchWords = _page.Server.HtmlEncode(_searchWords);
			else
			{
				//Just replace the script tag <> with HTML encoded &lt; and &gt;
				_searchWords = _searchWords.Replace( "<", "&lt;");
				_searchWords = _searchWords.Replace(">", "&gt;");
			}
			if (_dstPages == null)
				_dstPages = PagesDataset.Create();

            // check to see if we need to look in directory (which reloads the database table)
            // or just look in the database.
            Page srchFile;

            Database db = DatabaseFactory.CreateDatabase("SiteWorx");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_Paths_GetSearchableContent");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Asah.SiteWorx.Site.GetSite().ApplicationId);

            try
            {
                using (var dr = new SafeDataReader(db.ExecuteReader(dbCommand)))
                {
                    if (dr.Read())
                    {
                        // check for expiration for refreshing the database.
                        if (dr.GetDateTime("LastUpdated") < DateTime.Today.AddDays(-7))
                        {
                            // this code will run because the expiration date has expired 
                            // and so the normal search will take place.

                            // before the normal search occures we need to delete if there 
                            // are any existing records in the database.
                            ClearSearchableContent();
                            ProcessDirectory(targetDirectory);
                        }
                        else
                        {
                            srchFile = new Page();
                            srchFile.Path = dr.GetString("Path");
                            srchFile.Title = dr.GetString("Title");
                            srchFile.Description = dr.GetString("Description");
                            srchFile.Keywords = dr.GetString("Keywords");
                            srchFile.Size = dr.GetDecimal("Size");
                            srchFile.Contents = dr.GetString("Contents");
                            _totalFilesSearched += 1;

                            ProcessFile(srchFile);

                            while (dr.Read())
                            {
                                srchFile = new Page();
                                srchFile.Path = dr.GetString("Path");
                                srchFile.Title = dr.GetString("Title");
                                srchFile.Description = dr.GetString("Description");
                                srchFile.Keywords = dr.GetString("Keywords");
                                srchFile.Size = dr.GetDecimal("Size");
                                srchFile.Contents = dr.GetString("Contents");
                                _totalFilesFound += 1;
                                ProcessFile(srchFile);
                            }
                        }
                    }
                    else
                    {
                        // no records in database so we need to do the normal search.
                        // before the normal search occures we need to delete if there are any existing records in the database.
                        ClearSearchableContent();
                        ProcessDirectory(targetDirectory);
                    }
                }
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }
			return _dstPages;
		}

        public static void ClearSearchableContent()
        {
            Database db = DatabaseFactory.CreateDatabase("SiteWorx");
            DbCommand dbCommand = db.GetStoredProcCommand("aspnet_Paths_ClearSearchableContent");
            db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Asah.SiteWorx.Site.GetSite().ApplicationId);

            try
            {
                db.ExecuteNonQuery(dbCommand);
                dbCommand.Dispose();
            }
            catch (Exception ex)
            {
                bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                if (rethrow)
                {
                    throw;
                }
            }
        }
       
        /// <summary>
		/// Files in the directories are searched
		/// </summary>
		private void ProcessDirectory(string targetDirectory)
		{
			string[] fileEntries;
			string[] subdirectoryEntries;

			fileEntries = Directory.GetFiles(targetDirectory);
			foreach(string filePath in fileEntries)
			{
				_totalFilesSearched++;
				ProcessFile(filePath);
			}

			subdirectoryEntries = Directory.GetDirectories(targetDirectory);
			//Recurse into subdirectories of this directory
			foreach(string subdirectory in subdirectoryEntries)
			{
				//Check to make sure the folder about to be searched is not a barred folder if it is then don't search
				if(Site.BarredFolders.IndexOf(Path.GetFileName(subdirectory)) < 0)
				{
					ProcessDirectory(subdirectory);
				}
			}

		}

		/// <summary>
		/// Real logic for processing found files would go here.
		/// </summary>
		private void ProcessFile(string FPath)
		{
            Page srchFile;
            srchFile = GetInfo(FPath);
            ProcessFile(srchFile);
			
		}

        private void ProcessFile(Page srchFile)
        {
            if (srchFile != null)
			{
				srchFile.Search(_searchWords, _searchCriteria);
				if( srchFile.MatchCount > 0)
				{
					_totalFilesFound += 1;
					//Response.Write(srchFile.Contents)
					srchFile.CheckFileInfo();
					PagesDataset.StoreFile(_dstPages, srchFile);
				}
			}
        }

		/// <summary>
		/// File data is picked in this method
		/// </summary>
		private Page GetInfo(string FPath)
		{
			FileInfo fileInform = new FileInfo(FPath);
			Page srchFile = new Page();
			StringBuilder strBldFile = new StringBuilder();
			//Check the file extension to make sure the file is of the extension type to be searched
			if (Site.FilesTypesToSearch.IndexOf(fileInform.Extension) >= 0)
			{
				//_page.Trace.Warn("File ext.", fileInform.Extension);
				//Check to make sure the file about to be searched is not a barred file if it is don't search the file
				if (Site.BarredFiles.IndexOf(Path.GetFileName(FPath)) < 0)
				{
					//_page.Trace.Warn("File", FPath)
					if (!File.Exists(FPath))
					{
						//_page.Trace.Warn("Error", String.Format("{0} does not exist.", FPath))
						//Add throw excetion here
						//
						//
						return null;
					}
					GetFileInfo(FPath, srchFile);

					return srchFile;
				}
			}
			return null;
		}

		/// <summary>
		/// File path is modfied to be displayed as hyperlink in this method
		/// </summary>
		private void GetFilePath(string strFileURL, Page srchFile)
		{
			//Turn the server path to the file into a URL path to the file
			strFileURL = strFileURL.Replace(_page.Server.MapPath("./"), "");
			//Replace the NT backslash with the internet forward slash in the URL to the file
			strFileURL = strFileURL.Replace("\\", "/");
			//Encode the file name and path into the URL code method
			strFileURL = _page.Server.UrlEncode(strFileURL);
			//Just incase it's encoded any backslashes
			strFileURL = strFileURL.Trim().Replace("%2f", "/");
			srchFile.Path = strFileURL;
			//_page.Trace.Warn("Url", srchFile.Path)
		}

		/// <summary>
		/// File data is picked in this method
		/// </summary>

		private void GetFileInfo(string FPath , Page srchFile)
		{
			FileInfo fileInform = new FileInfo(FPath);
			StringBuilder strBldFile = new StringBuilder();
			decimal fileSize = fileInform.Length / 1024;
			srchFile.Size = fileSize;
			GetFilePath(FPath, srchFile);
			if (Site.DynamicFilesTypesToSearch.IndexOf(fileInform.Extension) >= 0 && fileInform.Extension != "")
			{
				_page.Trace.Warn("Path", String.Format("{0}/{1}", Site.DynamicFilesTypesToSearch, Site.DynamicFilesTypesToSearch.IndexOf(fileInform.Extension)));
				_page.Trace.Warn("Path", String.Format("{0}/{1}", "", srchFile.Path));
				GetDynamicFileContent(srchFile);
			}
			else
			{
				GetStaticFileContent(FPath, srchFile);
			}
			if(!srchFile.Contents.Equals(""))
			{
				//Read in the title of the file
				srchFile.Title = GetMetaContent(srchFile.Contents, "<title>", "</title>");
				//_page.Trace.Warn("Page Title", strPageTitle)
                //Read in the description meta tag of the file
				srchFile.Description = GetMetaContent(srchFile.Contents, "<meta name=\"description\" content=\"", "\" />");
				//_page.Trace.Warn("Page Desc", strPageDescription)
				//Read in the keywords of the file
				srchFile.Keywords = GetMetaContent(srchFile.Contents, "<meta name=\"keywords\" content=\"", "\" />");
				//_page.Trace.Warn("Page Keywords", strPageKeywords)
				srchFile.Contents = CleanHtml.Clean(srchFile.Contents);
				srchFile.Contents = strBldFile.AppendFormat("{0} {1} {2} {3}", srchFile.Contents, srchFile.Description, srchFile.Keywords, srchFile.Title).ToString().Trim();
				//_page.Trace.Warn("File Info", strBldFile.ToString) 
                

                // the following was added by Brian Lankford
                // this code is for inserting the page contents into the database when the site
                // is first searched.

                Database db = DatabaseFactory.CreateDatabase("SiteWorx");
                DbCommand dbCommand = db.GetStoredProcCommand("aspnet_Paths_CreateSearchableContent");
                db.AddInParameter(dbCommand, "ApplicationId", DbType.Guid, Asah.SiteWorx.Site.GetSite().ApplicationId);
                db.AddInParameter(dbCommand, "Path", DbType.String, srchFile.Path);
                db.AddInParameter(dbCommand, "Title", DbType.String, srchFile.Title);
                db.AddInParameter(dbCommand, "Description", DbType.String, srchFile.Description);
                db.AddInParameter(dbCommand, "Keywords", DbType.String, srchFile.Keywords);
                db.AddInParameter(dbCommand, "Contents", DbType.String, srchFile.Contents);
                db.AddInParameter(dbCommand, "Size", DbType.Decimal, srchFile.Size);
                db.AddInParameter(dbCommand, "LastUpdated", DbType.DateTime, DateTime.Now);

                try
                {
                    db.ExecuteNonQuery(dbCommand);
                    dbCommand.Dispose();
                }
                catch (Exception ex)
                {
                    bool rethrow = ExceptionPolicy.HandleException(ex, "Data Access Layer Policy");
                    if (rethrow)
                    {
                        throw;
                    }
                }
            }
		}
		///<summary>
		/// File Content is picked in this method
		///</summary>
		private void GetStaticFileContent(string FPath, Page srchFile)
		{
			StreamReader sr;
			sr = File.OpenText(FPath);
			try
			{
				srchFile.Contents = sr.ReadToEnd();
				sr.Close();
			}
			catch (Exception ex)
			{
				//_page.Trace.Warn("Error", ex.Message)
			}
		}

		///<summary>
		/// File Content is picked in this method
		///</summary>
		private void GetDynamicFileContent(Page srchFile)
		{
			System.Net.WebClient wcMicrosoft;
			UTF8Encoding objUTF8Encoding;
			wcMicrosoft = new System.Net.WebClient();
			objUTF8Encoding = new UTF8Encoding();
			srchFile.Contents = objUTF8Encoding.GetString(
				wcMicrosoft.DownloadData(String.Format("{0}/{1}", Site.ApplicationPath, srchFile.Path)));
		}

		///<summary>
		/// Metacontent is stripped in this method
		///</summary>
		private string GetMetaContent(string strFile, string strMetaStart, string strMetaEnd)
		{
			//List the text between the title tags:
			Regex regexp;
			string strMeta;
			string strPattern;
			string strInPattern;

			//If no description or keywords are found then you may be using http-equiv= instead of name= in your meta tags
			if (strFile.ToLower().IndexOf(strMetaStart) == -1 && strMetaStart.IndexOf("name=") != -1)
			{
				//Swap name= for http-equiv= 
				strMetaStart = strMetaStart.Replace("name=", "http-equiv=");
			}
			//Build Pattern
			strInPattern = "((.|\n)*?)";
			strPattern = String.Format("{0}{1}{2}", strMetaStart, strInPattern, strMetaEnd);
			regexp = new Regex(strPattern, RegexOptions.IgnoreCase);
			//Match Pattern
			strMeta = regexp.Match(strFile).ToString();
            strMeta = Regex.Replace(strMeta, "\\s+", " ");
			//Build Pattern
			strInPattern = "(.*?)";
			strPattern = String.Format("{0}{1}{2}", strMetaStart, strInPattern, strMetaEnd);
			//Get Pattern content
			strMeta = Regex.Replace(strMeta, strPattern, "$1", RegexOptions.IgnoreCase);
			return strMeta;
		}
	}
}
